<script>
import { Teleport, h } from 'vue'
import { createPopper } from '@popperjs/core'
import utils from '../common/utils'

export default {
    name: 'AScrollableH',
    props: {
        scrollbarWidth: {
            type: Number,
            default: 8
        },
        scrollbarDraggable: {
            type: Boolean,
            default: true
        },
        scrollbarOpacity: {
            type: Number,
            default: 0.5
        },
        scrollbarColor: {
            type: String,
            default: '#666'
        },
    },
    data() {
        return {
            popShown: false,
            prepareToHide: false,
            mouseDown: false,
            mouseIn: false,
            startScrollLeft: null,
            startMouseX: null,
            scacle: 1
        }
    },
    render() {
        let targetAttrs = Object.assign({
            class: 'c-scroll-hide'
        }, this.$attrs)
        let elList = [h(this.$slots.default()[0], targetAttrs)]
        if (this.popShown || this.mouseDown) {
            let popStyle = {
                opacity: this.scrollbarOpacity,
                height: this.scrollbarWidth + 'px',
                borderRadius: this.scrollbarWidth / 2 + 'px',
                backgroundColor: this.scrollbarColor,
            }

            let popRender = h('div', {
                ref: 'pop',
                style: popStyle,
                class: 'c-scrollbar'
            })

            elList.push(h(Teleport, {
                to: 'body'
            }, popRender))
        }

        return elList
    },
    mounted() {
        this.addEventListener()
    },
    watch: {
        popShown(val) {
            if (val) {
                this.onPopShown()
            } else {
                this.onPopHidden()
            }
        }
    },
    methods: {
        getTargetEl() {
            return utils.getEl(this.$el)
        },
        getPopEl() {
            return utils.getEl(this.$refs.pop)
        },
        addEventListener() {
            let targetEl = this.getTargetEl()
            targetEl.addEventListener('scroll', this.onScroll)
            targetEl.addEventListener('mouseover', this.onMouseover)
            targetEl.addEventListener('mouseleave', this.onMouseout)
            targetEl.addEventListener('click', this.onClick)
        },
        addPopEventListener() {
            let popEl = this.getPopEl()
            if (!popEl) {
                return
            }
            popEl.addEventListener('mousedown', this.onMousedown)
            this.addDocumentEventListener()
            popEl.addEventListener('mouseover', this.onMouseover)
            popEl.addEventListener('mouseout', this.onMouseout)
        },
        addDocumentEventListener() {
            window.document.addEventListener('mousemove', this.onMousemove)
            window.document.addEventListener('mouseup', this.onMouseup)
        },
        removeDocumentEventListener() {
            window.document.removeEventListener('mousemove', this.onMousemove)
            window.document.removeEventListener('mouseup', this.onMouseup)
        },
        onScroll() {
            this.pinToTarget()
        },
        onMousedown(e) {
            e.preventDefault()
            this.mouseDown = true
            let targetEl = this.getTargetEl()
            this.startScrollLeft = targetEl.scrollLeft
            this.startMouseX = e.clientX
        },
        onMousemove(e) {
            if (this.mouseDown) {
                e.preventDefault()
                let targetEl = this.getTargetEl()
                let newScrollLeft = (e.clientX - this.startMouseX) / this.scacle + this.startScrollLeft
                targetEl.scroll(newScrollLeft, 0, )
            }
        },
        onMouseup(e) {
            this.mouseDown = false
            if (this.mouseIn) {
                return
            }
            this.readyToHide()
        },
        onMouseover(e) {
            this.popShown = true
            this.prepareToHide = false
            this.mouseIn = true
        },
        onMouseout(e) {
            this.mouseIn = false
            if (this.mouseDown) {
                return
            }
            this.readyToHide()
        },
        readyToHide() {
            this.prepareToHide = true
            setTimeout(() => {
                if (this.prepareToHide) {
                    this.popShown = false
                }
            }, 50)
        },
        onClick(e) {
            let targetEl = this.getTargetEl()
            if (targetEl == null) {
                return
            }
            if (targetEl.clientWidth == targetEl.scrollWidth) {
                this.popShown = false
            } else {
                this.popShown = true
                this.prepareToHide = false
                this.pinToTarget()
            }
        },
        onPopShown() {
            this.$nextTick(() => {
                this.pinToTarget()
                if (this.scrollbarDraggable) {
                    this.addPopEventListener()
                }
            })
        },
        onPopHidden() {
            if (this.scrollbarDraggable) {
                this.removeDocumentEventListener()
            }
        },
        pinToTarget() {
            let popEl = this.getPopEl()
            if (!popEl) {
                return
            }

            let targetEl = this.getTargetEl()

            this.popInstance?.destroy()
            this.popInstance = createPopper(targetEl, popEl, {
                strategy: 'fixed',
                placement: 'bottom-start',
                modifiers: [
                    {
                        name: 'updatePopWidth',
                        enabled: true,
                        phase: 'main',
                        fn: ({ state }) => {
                            let { reference, popper } = state.elements
                            let scacle = reference.clientWidth / reference.scrollWidth
                            if (scacle >= 0.99 && scacle <= 1.01) {
                                this.popShown = false
                            } else {
                                let popWidth = reference.clientWidth * scacle
                                popper.style.width = popWidth + 'px'
                                let offsetX = reference.scrollLeft * scacle
                                state.options.modifiers.find(a => a.name == 'offset').options.offset[0] = offsetX
                            }
                            this.scacle = scacle
                        }
                    },
                    {
                        name: 'offset',
                        options: {
                            offset: [0, -this.scrollbarWidth]
                        },
                        requires: ['updatePopWidth']
                    }
                ]
            })
        },
    },
}
</script>